package uk.co.steamboy.utils
{
	import flash.events.*;
	import flash.utils.*;
	
	import uk.co.steamboy.events.*;

	public class DynamicTimer extends EventDispatcher
	{
		public var dynamicTimer:Timer;

	    private var _frameRate:int;
	    private var _period:Number;
	    private var _beforeTime:int = 0;
	    private var _afterTime:int = 0;
	    private var _timeDiff:int = 0;
	    private var _sleepTime:int = 0;
	    private var _overSleepTime:int = 0;
	    private var _excess:int = 0;
		private var _stopNextTimer:Boolean;
		
		private var _updaters:Array;
		private var _renderers:Array;
		
		public function DynamicTimer(frameRate:int = 40)
		{
			super();
			
			this.frameRate = frameRate;
			 
			 clearRegisters();
			 
		    _stopNextTimer = false;
			dynamicTimer = new Timer(_period, 1);
		    dynamicTimer.addEventListener(TimerEvent.TIMER, runTimer);
		}
		
		public function get frameRate():int { return _frameRate; }
		public function set frameRate(value:int):void 
		{
			_frameRate 	= value;
			_period 	= 1000 / value;
		}
		
		public function start():void
		{
		    _stopNextTimer = false;
		    dynamicTimer.start();
		}
		
		public function stop():void
		{
			_stopNextTimer = true;
		}
		
		public function registerUpdater(updater:Function):void
		{
			_updaters.push(updater);
		}
		
		public function removeUpdater(updater:Function):void
		{
			var len:int = _updaters.length;
			for (var i:int = 0; i < len; i++)
			{
				if (_updaters[i] == updater) 
				{
					_updaters.splice(i, 1);
					break;			
				}
			}	
		}
		
		public function registerRenderer(renderer:Function):void
		{
			_renderers.push(renderer);
		}
		
		public function removeRenderer(renderer:Function):void
		{
			var len:int = _renderers.length;
			for (var i:int = 0; i < len; i++)
			{
				if (_renderers[i] == renderer) 
				{
					_renderers.splice(i, 1);
					break;			
				}
			}	
		}
				
		public function clearRegisters():void
		{
			_updaters = [];
			_renderers = [];
		}
		
		private function runTimer (event:TimerEvent):void
		{
			//trace("runTimer...");
			
			if (_stopNextTimer) return;
			
		    _beforeTime = getTimer();
		    _overSleepTime = (_beforeTime - _afterTime) - _sleepTime;
		
			update();
			render();
			
		    _afterTime = getTimer();
		    _timeDiff = _afterTime - _beforeTime;
		    _sleepTime = (_period - _timeDiff) - _overSleepTime;        
		    if (_sleepTime <= 0) {
		        _excess -= _sleepTime
		        _sleepTime = 2;
		    }        
		    dynamicTimer.reset();
		    dynamicTimer.delay = _sleepTime;
		    dynamicTimer.start();
		
		    while (_excess > _period) 
		    {
		        update();
		        _excess -= _period;
		    }
		    
		    event.updateAfterEvent();
		}
		
		private function update():void
		{
			var len:int = _updaters.length;
			for (var i:int = 0; i < len; i++)
			{
				_updaters[i]();
			}
			dispatchEvent(new DynamicTimerEvent(DynamicTimerEvent.UPDATE));		
		}
		
		private function render():void
		{
			var len:int = _renderers.length;
			for (var i:int = 0; i < len; i++)
			{
				_renderers[i]();
			}
			dispatchEvent(new DynamicTimerEvent(DynamicTimerEvent.RENDER));
		} 
	}
}